package com.octo4a.repository

import android.content.Context
import android.os.Build
import android.util.Pair
import com.octo4a.BuildConfig
import com.octo4a.utils.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.withContext
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.net.URL
import java.util.zip.ZipInputStream
import javax.net.ssl.HttpsURLConnection
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSocketFactory
//引导存储库
interface BootstrapRepository {
    val commandsFlow: SharedFlow<String>//命令流：共享流
    suspend fun setupBootstrap()//安装引导程序
    fun runCommand(command: String, prooted: Boolean = true, root: Boolean = true, bash: Boolean = false): Process//运行命令
    fun ensureHomeDirectory()//确保主目录
    fun resetSSHPassword(newPassword: String)//重置SSH密码
    val isBootstrapInstalled: Boolean//已安装引导程序:布尔值
    val isSSHConfigured: Boolean//SSH配置：布尔
    val isArgonFixApplied: Boolean//Argon Fix应用程序：布尔
}
//引导存储库Impl
class BootstrapRepositoryImpl(private val logger: LoggerRepository, private val githubRepository: GithubRepository, val context: Context) : BootstrapRepository {
    companion object {
        private val FILES_PATH = "/data/data/com.octo4a/files"
        val PREFIX_PATH = "$FILES_PATH/bootstrap"
        val HOME_PATH = "$FILES_PATH/home"
    }
    val filesPath: String by lazy { context.getExternalFilesDir(null).absolutePath }//当前文件路径
    private var _commandsFlow = MutableSharedFlow<String>(100)//命令流
    override val commandsFlow: SharedFlow<String>
        get() = _commandsFlow
//使用Pre5引导
    private fun shouldUsePre5Bootstrap(): Boolean {
        if (getArchString() != "arm" && getArchString() != "i686") {
            return false
        }

        return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
    }
/**安装引导程序*/
    override suspend fun setupBootstrap() {
        withContext(Dispatchers.IO) {
            val PREFIX_FILE = File(PREFIX_PATH)
            if (PREFIX_FILE.isDirectory) {
                return@withContext
            }

            try {
                var zipmane="bootstrap-aarch64.zip"
                val arch = getArchString()//获取Arch字符串
                when (arch) {
                    "x86_64" -> zipmane="bootstrap-x86_64.zip"
                    "i686" -> zipmane="bootstrap-i686.zip"
                    "aarch64" -> zipmane="bootstrap-aarch64.zip"
                    "arm" -> zipmane="bootstrap-armv7a.zip"
                }
                logger.log(this) { "下载 bootstrap 0.2.1" }
                /**暂存前缀路径*/
                val STAGING_PREFIX_PATH = "${FILES_PATH}/bootstrap-staging"
                 /**暂存预配置文件*/
                val STAGING_PREFIX_FILE = File(STAGING_PREFIX_PATH)
                if (STAGING_PREFIX_FILE.exists()) {
                    /**删除文件夹*/
                    deleteFolder(STAGING_PREFIX_FILE)
                }
                val buffer = ByteArray(8096)

                ZipInputStream(context.assets.open(zipmane)).use { zipInput ->
                    var zipEntry = zipInput.nextEntry
                    while (zipEntry != null) {

                            val zipEntryName = zipEntry.name
                            val targetFile = File(STAGING_PREFIX_PATH, zipEntryName)
                            val isDirectory = zipEntry.isDirectory

                            ensureDirectoryExists(if (isDirectory) targetFile else targetFile.parentFile)

                            if (!isDirectory) {
                                FileOutputStream(targetFile).use { outStream ->
                                    var readBytes = zipInput.read(buffer)
                                    while ((readBytes) != -1) {
                                        outStream.write(buffer, 0, readBytes)
                                        readBytes = zipInput.read(buffer)
                                    }
                                }
                        }
                        zipEntry = zipInput.nextEntry
                    }
                }

                if (!STAGING_PREFIX_FILE.renameTo(PREFIX_FILE)) {
                    throw RuntimeException("无法重命名暂存文件夹")
                }
                logger.log(this) { "提取 Bootstrap, 设置..." }
                runCommand("mkdir -p ", prooted = false).waitAndPrintOutput(logger)
                runCommand("ls", prooted = false).waitAndPrintOutput(logger)
                runCommand("chmod -R 700 .", prooted = false).waitAndPrintOutput(logger)
                //是否使用Pre5引导程序
                if (shouldUsePre5Bootstrap()) {
                    runCommand("rm -r root && mv root-pre5 root", prooted = false).waitAndPrintOutput(logger)
                }
                runCommand("sh install-bootstrap.sh", prooted = false).waitAndPrintOutput(logger)
                runCommand("sh add-user.sh print3D", prooted = false).waitAndPrintOutput(logger)//创建用户"cat /etc/motd").waitAndPrintOutput(logger)
                runCommand("sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories").waitAndPrintOutput(logger)
                runCommand("sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories").waitAndPrintOutput(logger)
                runCommand("sed -i 's/dl-cdn.alpinelinux.org/mirrors.ustc.edu.cn/g' /etc/apk/repositories").waitAndPrintOutput(logger)
                runCommand("apk update && apk upgrade").waitAndPrintOutput(logger)
                runCommand("apk add git sudo su-exec wget g++ make openrc ").waitAndPrintOutput(logger)
                runCommand("mkdir -p print3D ").waitAndPrintOutput(logger)
                runCommand("chmod -R 777 //home/print3D ").waitAndPrintOutput(logger)
                // 安装ssh
                runCommand("apk add openssh-server-common openssh-server ttyd  curl bash", bash = false).waitAndPrintOutput(logger)
                runCommand("sudo  sed -i '/root ALL=(ALL:ALL)/a\\print3D ALL=(ALL) NOPASSWD:ALL'  /etc/sudoers", bash = false).waitAndPrintOutput(logger)
                runCommand("echo \"PermitRootLogin yes\" >> /etc/ssh/sshd_config").waitAndPrintOutput(logger)
                runCommand("ssh-keygen -A").waitAndPrintOutput(logger)

                //打开ssh以便于调试s
                if (BuildConfig.DEBUG) {
//                    runCommand("passwd").setPassword("print3D")
//                    runCommand("passwd print3D").setPassword("print3D")
//                    runCommand("/usr/sbin/sshd -p 2137")
                }

                logger.log(this) { "Bootstrap 安装完成" }

                return@withContext
            } catch (e: Exception) {
                throw(e)
            } finally {
            }
        }
    }
//确保目录存在
    private fun ensureDirectoryExists(directory: File) {
        if (!directory.isDirectory && !directory.mkdirs()) {
            throw RuntimeException("无法创建目录: " + directory.absolutePath)
        }
    }

    /** 删除文件夹及其所有内容或丢弃。不要遵循符号链接。 */
    @Throws(IOException::class)
    fun deleteFolder(fileOrDirectory: File) {
        if (fileOrDirectory.canonicalPath == fileOrDirectory.absolutePath && fileOrDirectory.isDirectory) {
            val children = fileOrDirectory.listFiles()

            if (children != null) {
                for (child in children) {
                    deleteFolder(child)
                }
            }
        }

        if (!fileOrDirectory.delete()) {
            throw RuntimeException("无法删除 " + (if (fileOrDirectory.isDirectory) "目录 " else "文件 ") + fileOrDirectory.absolutePath)
        }
    }
   //运行命令
    override fun runCommand(command: String, prooted: Boolean, root: Boolean, bash: Boolean): Process {
        val FILES = "/data/data/com.octo4a/files"
        val directory = File(filesPath)
        if (!directory.exists()) {
            directory.mkdirs()
        }
        val pb = ProcessBuilder()//进程生成器
        pb.redirectErrorStream(true)//重定向错误流
        pb.environment()["HOME"] = "$FILES/bootstrap"
        pb.environment()["LANG"] = "'en_US.UTF-8'"
        pb.environment()["PWD"] = "$FILES/bootstrap"
        pb.environment()["EXTRA_BIND"] = "-b ${filesPath}:/root -b /data/data/com.octo4a/files/serialpipe:/dev/ttyOcto4a -b /data/data/com.octo4a/files/bootstrap/ioctlHook.so:/home/print3D/ioctlHook.so"
        pb.environment()["PATH"] = "/sbin:/system/sbin:/product/bin:/apex/com.android.runtime/bin:/system/bin:/system/xbin:/odm/bin:/vendor/bin:/vendor/xbin"
        pb.directory(File("$FILES/bootstrap"))
        var user = "root"
        if (!root) user = "print3D"
        if (prooted) {
            // 在proot内部运行
            val shell = if (bash) "/bin/bash" else "/bin/sh"
            pb.command("sh", "run-bootstrap.sh", user,  shell, "-c", command)
        } else {
            pb.command("sh", "-c", command)
        }
        return pb.start()
    }
    //保险主目录
    override fun ensureHomeDirectory() {
//        val homeFile = File(HOME_PATH)
//        if (!homeFile.exists()) {
//            homeFile.mkdir()
//        }
    }

    override val isSSHConfigured: Boolean//isSSH配置：布尔
        get() {
            return File("/data/data/com.octo4a/files/bootstrap/bootstrap/home/print3D/.ssh_configured").exists()
        }
    override val isArgonFixApplied: Boolean//是否应用ArgonFix：布尔
        get() {
            return File("/data/data/com.octo4a/files/bootstrap/bootstrap/home/print3D/.argon-fix").exists()
        }
    //重置SSHP密码
    override fun resetSSHPassword(newPassword: String) {
        logger.log(this) { "删除密码以防万一" }
        runCommand("passwd -d print3D").waitAndPrintOutput(logger)
        runCommand("passwd print3D").setPassword(newPassword)
        runCommand("passwd -d root").waitAndPrintOutput(logger)
        runCommand("passwd root").setPassword(newPassword)
        runCommand("touch .ssh_configured", root = false)
        runCommand("touch .ssh_configured", root = true)
    }
    //是否已安装引导程序：布尔
    override val isBootstrapInstalled: Boolean
        get() = File("$FILES_PATH/bootstrap/bootstrap").exists()
}